#include "NoiseGenerator.h"


#include <math.h>
#include <iostream>


namespace Y{

float NoiseGenerator::Noise[NOISE_SIZE][NOISE_SIZE][20];
uint NoiseGenerator::seed;

NoiseGenerator::NoiseGenerator():
	persistence(0.75),
	octaves(5),
	xOffset(1),
	yOffset(1),
	scale(0.02)
{
	static bool first = true;
	if ( first ){
		generateNoise( time(NULL) );
		first = false;
	}
}

void NoiseGenerator::generateNoise (uint s){
	if ( s == 0 ){ seed = time(NULL); }
	else { seed = s; }
	
	srand(seed);
	
	for (int i = 0; i < NOISE_SIZE; ++i){
		for (int j = 0; j < NOISE_SIZE; ++j){
			for (int q = 0; q < 20; ++q){
				Noise[i][j][q] = float(rand() % 10000) / 5000.0 - 1.0;
			}
		}
	}

}

float NoiseGenerator::noise (int x, int y, int o){
	x += xOffset;
	y += yOffset;

	if ( y >= 0 && x >= 0 && y < NOISE_SIZE && x < NOISE_SIZE){
		float total = Noise[x][y][o] + 
						Noise[x + 1][y][o] / 2.0 + Noise[x][y + 1][o] / 2.0 + Noise[x-1][y][o] / 2.0 + Noise[x][y-1][o] / 2.0 + 
						Noise[x + 1][y + 1][o] / 4.0 + Noise[x + 1][y - 1][o] / 4.0 + Noise[x - 1][y - 1][o] / 4.0 + Noise[x - 1][y + 1][o] / 4.0;
		return (total / 4.0);
	}
	return 0.5;
}

float NoiseGenerator::noise (int x, int o){
	
	if ( x < 0 || x >= NOISE_SIZE ){ return 0.5; }
	
	return ((Noise[x][0][o] + Noise[x + 1][0][o] / 2.0 + Noise[x - 1][0][o] / 2.0) / 2.0);
	
}


float NoiseGenerator::noise1d (float x,int o){
	int ix = (int) x;
	float fx = x - (float) ix;
	
	return cosineInterpolation( noise(ix,o), noise(ix + 1,o), fx );
}

	float NoiseGenerator::cosineInterpolation(float a, float b, float x){
		 float f = (1.0 - cos(x * 3.1415927)) * 0.5;
		 return ( a * (1.0 - f) + b * f );
	}
	
float NoiseGenerator::noise2d (float x, float y, int o){

	int ix = (int) x,iy = (int) y;
	float fx = x - (float) ix,fy = y - (float) iy;
	
	float r1 = cosineInterpolation( noise(ix,iy,o), noise(ix + 1,iy,o), fx );
	float r2 = cosineInterpolation( noise(ix,iy + 1,o), noise(ix + 1,iy + 1,o), fx );
	
	return cosineInterpolation( r1, r2, fy );

}

float NoiseGenerator::perlinNoise1d (float x){
	if ( x > NOISE_SIZE ){ x = (int) x % NOISE_SIZE; }
	if (octaves > 20){ std::cout << "\n" << "Too many octaves, reseting to 20"; octaves = 20; }
	float total = 0.0;
	
	for (int i = 0; i < octaves; ++i){
		
		float frequency = 1 << i;
		float amplitude = pow(persistence,i);
		
		total += noise1d(x * frequency,i) * amplitude;
		
	}

	return total;
}

float NoiseGenerator::perlinNoise2d (float x, float y){
	x *= scale;
	y *= scale;
	if ( x > NOISE_SIZE ){ x = (int) x % NOISE_SIZE; }
	if ( y > NOISE_SIZE ){ y = (int) y % NOISE_SIZE; }
	if (octaves > 20){ std::cout << "\n" << "Too many octaves, reseting to 20"; octaves = 20; }
	float total = 0.0;
	
	for (int i = 0; i < octaves; ++i){
		
		float frequency = 1 << i;
		float amplitude = pow(persistence,i);
		
		total += noise2d(x * frequency,y * frequency,i) * amplitude;
		
	}
	
	return total;
}


float NoiseGenerator::fractalSumNoise2d (int I, float x,float y){
	float r = 0.0;
	for (int i = 0; i < I; ++i){
		r += (perlinNoise2d(x * (float)(1 << i),y * (float)(1 << i)) / (float)(1 << i));
	}
	return r;
}

float NoiseGenerator::fractalSumNoise2d_abs (int I, float x,float y){
	float r = 0.0;
	for (int i = 0; i < I; ++i){
		r += fabsf(perlinNoise2d(x * (float)(1 << i),y * (float)(1 << i)) / (float)(1 << i));
	}
	return r;
}

}